package main

import (
	"bytes"
	"fmt"
	"reflect"
)

/**
 * 切片的基本使用
    Go 语言切片是对数组的抽象。
    Go 数组的长度不可改变，在特定场景中这样的集合就不太适用，Go中提供了一种灵活，功能强悍的内置类型切片("动态数组"),
     与数组相比切片的长度是不固定的，可以追加元素，在追加时可能使切片的容量增大。

   从概念上面来说slice像一个结构体，这个结构体包含了三个元素：
		指针，指向数组中slice指定的开始位置
		长度，即slice的长度
		最大长度，也就是slice开始位置到数组的最后位置的长度

   Go 是如何实现切片类型的？
	type slice struct {
    	array unsafe.Pointer
   	 	len   int
    	cap   int
	}
    array: 是指向底层数组的指针
    len: 是切片的长度，即切片中当前元素的个数
    cap: 是底层数组的长度，也是切片的最大容量，cap 值永远大于等于 len 值。
*/
func main() {

	// 定义切片  通过 make 函数来创建切片，并指定底层数组的长度
	var slice01 []int = make([]int, 10)
	// slice02 := make([]int, 10)

	// 其中10为cap值，即底层数组长度，6为切片的初始长度
	sl01 := make([]byte, 6, 10)
	fmt.Println(sl01)

	for i := 0; i < 10; i++ {
		slice01[i] = i
	}

	var b []int = slice01[1:4] //creates a slice from a[1] to a[3]
	fmt.Println(b)

	// 修改切片
	darr := [...]int{57, 89, 90, 82, 100, 78, 67, 69, 59}

	// 采用 array[low : high : max]语法基于一个已存在的数组创建切片。这种方式被称为数组的切片化
	// 基于数组创建的切片，它的起始元素从 low 所标识的下标值开始，切片的长度（len）是 high - low，它的容量是 max - low
	sl02 := darr[3:7:9]
	fmt.Println(sl02)

	dslice := darr[2:5]
	fmt.Println("array before", darr)
	for i := range dslice {
		dslice[i]++
	}
	fmt.Println("array after", darr)

	// 当多个片共享相同的底层数组时，每个元素所做的更改将在数组中反映出来。
	numa := [3]int{78, 79, 80}
	nums1 := numa[:] //creates a slice which contains all elements of the array
	nums2 := numa[:]
	fmt.Println("array before change 1", numa)
	nums1[0] = 100
	fmt.Println("array after modification to slice nums1", numa)
	nums2[1] = 101
	fmt.Println("array after modification to slice nums2", numa)

	/**
	 * 切片的长度是切片中元素的数量。切片的容量是从创建切片的索引开始的底层数组中元素的数量
	 * 切片是可索引的，并且可以由 len() 方法获取长度
	 */
	var numbers = make([]int, 3, 5)
	printSlice(numbers)

	/* 创建切片 */
	numbers02 := []int{0, 1, 2, 3, 4, 5, 6, 7, 8}
	printSlice(numbers02)

	/* 打印原始切片 */
	fmt.Println("numbers02 ==", numbers02)

	/* 打印子切片从索引1(包含) 到索引4(不包含)*/
	fmt.Println("numbers02[1:4] ==", numbers02[1:4])

	/* 默认下限为 0*/
	fmt.Println("numbers02[:3] ==", numbers02[:3])

	/* 默认上限为 len(s)*/
	fmt.Println("numbers02[4:] ==", numbers02[4:])

	/* 打印子切片从索引  0(包含) 到索引 2(不包含) */
	numbers03 := numbers02[:2]
	printSlice(numbers03)

	/* 打印子切片从索引 2(包含) 到索引 5(不包含) */
	numbers04 := numbers02[2:5]
	printSlice(numbers04)

	/*
	 * append() 和 copy() 函数
	 * append 向slice里面追加一个或者多个元素，然后返回一个和slice一样类型的slice
	 * copy 函数copy从源slice的src中复制元素到目标dst，并且返回复制的元素的个数
	 */
	fmt.Println("append copy func operate")
	var numbers05 []int
	printSlice(numbers05)

	/* 允许追加空切片 */
	numbers05 = append(numbers05, 0)
	printSlice(numbers05)

	/* 向切片添加一个元素 */
	numbers05 = append(numbers05, 1)
	printSlice(numbers05)

	/* 同时添加多个元素 */
	numbers05 = append(numbers05, 2, 3, 4)
	printSlice(numbers05)

	/* 创建切片 numbers1 是之前切片的两倍容量*/
	numbers06 := make([]int, len(numbers05), (cap(numbers05))*2)

	/* 拷贝 numbers 的内容到 numbers1 */
	copy(numbers06, numbers05)
	printSlice(numbers06)

	// append 操作
	number07 := make([]int, 32)
	// 把 number07[1:16] 的切片赋给到了 number08
	number08 := number07[1:16]
	// 对 number07做了一个 append()的操作，这个操作会让 number07 重新分享内存
	number07 = append(number07, 1)
	number07[2] = 42

	printSlice(number07)
	printSlice(number08)

	/*
		dir01 和 dir02 共享内存，虽然 dir01 有一个 append() 操作，但是因为 cap 足够，于是数据扩展到了dir02 的空间
	*/
	path := []byte("AAAA/BBBBBBBBB")
	sepIndex := bytes.IndexByte(path, '/')

	// dir01 :=path[:sepIndex]
	// 如果不想dir01的append扩展到dir02的空间，使用如下
	dir01 := path[:sepIndex:sepIndex]
	dir02 := path[sepIndex+1:]

	fmt.Println("dir01 =>", string(dir01)) //prints: dir01 => AAAA
	fmt.Println("dir02 =>", string(dir02)) //prints: dir02 => BBBBBBBBB

	dir01 = append(dir01, "suffix"...)

	fmt.Println("dir01 =>", string(dir01)) //prints: dir01 => AAAAsuffix
	fmt.Println("dir02 =>", string(dir02)) //prints: dir02 => uffixBBBB

	// 深度比较
	/*
		当遇到复杂一个对象时，这个对象可以是内建数据类型，数组，结构体，map……我们在复制结构体的时候，需要比较两个结构体中的数据是否相同时，
		需要使用深度比较，而不是只是简单地做浅度比较。这里需要使用到反射 reflect.DeepEqual()
	*/

	val01 := data{}
	val02 := data{}
	fmt.Println("val01 == val02:", reflect.DeepEqual(val01, val02)) // val01 == val02: true

	m1 := map[string]string{"one": "a", "two": "b"}
	m2 := map[string]string{"two": "b", "one": "a"}
	fmt.Println("m1 == m2:", reflect.DeepEqual(m1, m2)) // m1 == m2: true

	s1 := []int{1, 2, 3}
	s2 := []int{1, 2, 3}
	fmt.Println("s1 == s2:", reflect.DeepEqual(s1, s2)) // s1 == s2: true

	// 切片的动态扩容

	var s []int
	s = append(s, 11)
	fmt.Println(len(s), cap(s)) //1 1
	s = append(s, 12)
	fmt.Println(len(s), cap(s)) //2 2
	s = append(s, 13)
	fmt.Println(len(s), cap(s)) //3 4
	s = append(s, 14)
	fmt.Println(len(s), cap(s)) //4 4
	s = append(s, 15)
	fmt.Println(len(s), cap(s)) //5 8

	// 基于一个已有数组建立的切片，一旦追加的数据操作触碰到切片的容量上限（实质上也是数组容量的上界)，
	// 切片就会和原数组解除“绑定”，后续对切片的任何修改都不会反映到原数组中。
	u := [...]int{11, 12, 13, 14, 15}
	fmt.Println("array:", u) // [11, 12, 13, 14, 15]
	sl03 := u[1:3]
	fmt.Printf("slice(len=%d, cap=%d): %v\n", len(sl03), cap(sl03), sl03) // [12, 13]

	sl03 = append(s, 24)
	// after append 24, array: [11 12 13 24 15]
	fmt.Println("after append 24, array:", u)
	// // after append 24, slice(len=3, cap=4): [12 13 24]
	fmt.Printf("after append 24, slice(len=%d, cap=%d): %v\n", len(sl03), cap(sl03), sl03)

	sl03 = append(s, 25)
	// after append 25, array: [11 12 13 24 25]
	fmt.Println("after append 25, array:", u)
	// after append 25, slice(len=4, cap=4): [12 13 24 25]
	fmt.Printf("after append 25, slice(len=%d, cap=%d): %v\n", len(sl03), cap(sl03), sl03)

	sl03 = append(s, 26)
	// after append 26, array: [11 12 13 24 25]
	fmt.Println("after append 26, array:", u)
	// after append 26, slice(len=5, cap=8): [12 13 24 25 26]
	fmt.Printf("after append 26, slice(len=%d, cap=%d): %v\n", len(sl03), cap(sl03), sl03)

	sl03[0] = 22
	// after reassign 1st elem of slice, array: [11 12 13 24 25]
	fmt.Println("after reassign 1st elem of slice, array:", u)
	// after reassign 1st elem of slice, slice(len=5, cap=8): [22 13 24 25 26]
	fmt.Printf("after reassign 1st elem of slice, slice(len=%d, cap=%d): %v\n", len(sl03), cap(sl03), sl03)

	fmt.Println("start: go 支持将切片转换为数组指针")

	// 数组转为切片
	arr := [3]int{0, 1, 2}
	// 通过切片化将数组arr转换为切片sl04
	sl04 := arr[:]
	sl04[1] += 10
	// [0 11 2]
	fmt.Printf("original.arr : %v\n", arr)

	// 将一个切片转换为数组类型指针
	sl05 := []int{11, 12, 13}
	// 将切片转换为数组类型指针
	// 注意 转换后的数组长度不能大于原切片的长度
	arr01 := (*[3]int)(sl05)
	arr01[1] = arr01[1] + 10
	fmt.Printf("transformed.arr : %v\n", arr01)
	fmt.Println("end: go 支持将切片转换为数组指针")

}

type data struct {
}

/**
 * 打印切片信息
 */
func printSlice(x []int) {
	fmt.Printf("len=%d cap=%d slice=%v\n", len(x), cap(x), x)
}
